# -*- coding: utf-8 -*-
import os
import math


# font format const
FONT_SIZE = 0
FONT_FAMILYNAME = 1
FONT_FONTBOUNDINGBOX = 2
FONT_CHARS = 3
FONT_WORDLIST = 4

# char format const
CHAR_UNICODE = 0
CHAR_DWIDTH = 1
CHAR_BBX = 2
CHAR_BITMAP = 3

# bdf format const
BDF_VERSION = b'2.1'
BDF_SIZE = b'SIZE '
BDF_FAMILYNAME = b'FAMILY_NAME '
BDF_FONTBOUNDINGBOX = b'FONTBOUNDINGBOX '
BDF_STARTCHAR = b'STARTCHAR '
BDF_ENDCHAR = b'ENDCHAR'
BDF_STARTFONT = b'STARTFONT '
BDF_DWIDTH = b'DWIDTH '
BDF_BBX = b'BBX '
BDF_BITMAP = b'BITMAP'

# BDF_FILENAME = 'bdf_sample.bdf'
BDF_FILENAME = 'SIMYOU_U24.bdf'

def output_glyph(filename, glyph_list):
	header = f'# GENERATED BY {os.path.basename(__file__)}'
	word_list = f'# 文字列表:\n# {glyph_list[0]}'
	word_utf8_list = f'# 文字 Unicode 列表:\n# {[get_utf8(unicode) for unicode in glyph_list[0]]}'

	with open(filename, 'w', encoding='utf-8') as output_file:
		output_file.writelines(
f'''{header}

{word_list}

{word_utf8_list}

class Font(object):
	def __init__(self):
		self.__width = {glyph_list[1][0]}
		self.__height = {glyph_list[1][1]}
		self.__offset_x = {glyph_list[1][2]}
		self.__offset_y = {glyph_list[1][3]}
		self.__count = {glyph_list[2]}
		self.__glyphs = {[glyph for glyph in glyph_list[3:]]}

	@property
	def width(self):
		return self.__width

	@property
	def height(self):
		return self.__height

	@property
	def offset_x(self):
		return self.__offset_x

	@property
	def offset_y(self):
		return self.__offset_y

	@property
	def count(self):
		return self.__count

	def get_glyph(self, char):
		if char:
			char_utf8 = self.get_utf8(char)
			for glyph in self.__glyphs:
				if glyph[0] == char_utf8:
					return [glyph[1], glyph[2], glyph[3]]

			return self.get_glyph('？')

	def get_glyph_utf8(self, char_utf8):
		if char_utf8:
			for glyph in self.__glyphs:
				if glyph[0] == char_utf8:
					return [glyph[1], glyph[2], glyph[3]]

			return self.get_glyph_utf8('？')

	def get_utf8(self, char):
		char_utf8 = 0x00
		code = char.encode('utf-8')

		char_utf8 |= code[0] << 16
		char_utf8 |= code[1] << 8
		char_utf8 |= code[2]

		return hex(char_utf8)


if __name__ == '__main__':
	font = Font()

	word = '{list(glyph_list[0])[0]}'
	glyph = font.get_glyph(word)

	print('===============')
	print('glyphs info:')
	print('font')
	print('    width: {{}}, height: {{}}, count: {{}}'.format(font.width, font.height, font.count))
	print('glyph')
	print('    width: {{}}, height: {{}}'.format(glyph[0][0], glyph[0][1]))
	print('[{{}}] glyph: {{}}'.format(word, glyph))
''')

		output_file.flush()

def get_utf8(char):
	char_utf8 = 0x00
	code = char.encode('utf-8')

	char_utf8 |= code[0] << 16
	char_utf8 |= code[1] << 8
	char_utf8 |= code[2]

	return hex(char_utf8)

def convert_1(bdf_list):
	width = bdf_list[FONT_FONTBOUNDINGBOX][0]
	height = bdf_list[FONT_FONTBOUNDINGBOX][1]
	chars = len(bdf_list[5:])
	word_list = bdf_list[FONT_WORDLIST]

	"""
	glyph 格式
		1. 文字列表, ['我', ...]
		2. 字体宽度, 24
		3. 字体高度, 26
		4. 字形总数, 8
		5. 单字形数组, 格式见下方注释
	"""
	glyph_list = [word_list, width, height, chars]

	for item in bdf_list[5:]:
		unicode = item[CHAR_UNICODE]
		bbx_width = item[CHAR_BBX][0]
		bbx_height = item[CHAR_BBX][1]
		bbx_off_x = item[CHAR_BBX][2]
		bbx_off_y = item[CHAR_BBX][3]
		dwidth_x = item[CHAR_DWIDTH]
		pages = height // 8 + 1 if height % 8 > 0 else height // 8
		byte_length = 8
		buffer = bytearray(pages * width)

		# print(f'glyph {unicode} info:\n    width: {width}\n    height: {height}\n    pages: {pages}')

		dot_matrix = []
		for line in item[CHAR_BITMAP]:
			if bbx_width + byte_length < width:
				temp = int(line, 16)
				if bbx_off_x > 0:
					temp <<= abs(bbx_off_x)
				else:
					temp >>= abs(bbx_off_x)

				line = bin(temp).replace('0b', '').zfill(width)
			else:
				line = bin(int(line, 16)).replace('0b', '').zfill(width)

			dot_matrix.append(line)
		print(dot_matrix)
		# for line in dot_matrix:
		# 	print(line)

		new_dot_matrix = []
		for page in range(pages):
			for x in range(width):
				new_line = ''
				for y in range(byte_length - 1, -1, -1):
					try:
						new_line += dot_matrix[y + page * byte_length][x]
					except IndexError:
						new_line += '0'
				new_dot_matrix.append(new_line)

		# print(new_dot_matrix)
		for count, new_line in enumerate(new_dot_matrix):
			buffer[count] = int(new_line, 2)

		"""
		单字形数组格式
			1. 紧凑型 utf-8 编码, 0xe59bbd
			2. BBX, [8, 17, 8, -1]
				a. BBX[0]: 实际宽度
				b. BBX[1]: 实际高度
				c. BBX[2]: x 轴偏移量，正数向右，负数向左
				d. BBX[3]: y 轴偏移量，正数向上，负数向下
			3. DWIDTH 距离下一个字形原点的水平间距, 16
			4. 符合 FrameBuffer 格式的 bytearray, bytearray(b'\x00\x03')
		"""
		glyph_list.append([unicode, [bbx_width, bbx_height, bbx_off_x, bbx_off_y], dwidth_x, buffer])

	return glyph_list

def convert(bdf_list):
	width = bdf_list[FONT_FONTBOUNDINGBOX][0]
	height = bdf_list[FONT_FONTBOUNDINGBOX][1]
	offset_x = bdf_list[FONT_FONTBOUNDINGBOX][2]
	offset_y = bdf_list[FONT_FONTBOUNDINGBOX][3]
	chars = len(bdf_list[5:])
	word_list = bdf_list[FONT_WORDLIST]

	"""
	glyph 格式
		1. 文字列表, ['我', ...]
		2. FONTBOUNDINGBOX, [24, 26, 0, -6]
			1. 字体宽度, 24
			2. 字体高度, 26
			3. X 偏移量, 0
			4. Y 偏移量, -6
		3. 字形总数, 8
		4. 单字形数组, 格式见下方注释
	"""
	glyph_list = [word_list, [width, height, offset_x, offset_y], chars]

	for item in bdf_list[5:]:
		unicode = item[CHAR_UNICODE]
		bbx_width = item[CHAR_BBX][0]
		bbx_height = item[CHAR_BBX][1]
		bbx_off_x = item[CHAR_BBX][2]
		bbx_off_y = item[CHAR_BBX][3]
		dwidth_x = item[CHAR_DWIDTH]
		pages = bbx_height // 8 + 1 if bbx_height % 8 > 0 else bbx_height // 8
		byte_length = 8
		buffer = bytearray(pages * bbx_width)

		# print(f'glyph {unicode} info:\n    width: {width}\n    height: {height}\n    pages: {pages}')

		dot_matrix = []
		padding = math.ceil(bbx_width / byte_length)
		for line in item[CHAR_BITMAP]:
			# if bbx_width + byte_length < width:
			# 	temp = int(line, 16)
			# 	if bbx_off_x > 0:
			# 		temp <<= abs(bbx_off_x)
			# 	else:
			# 		temp >>= abs(bbx_off_x)

			# 	line = bin(temp).replace('0b', '').zfill(width)
			# else:
			line = bin(int(line, 16)).replace('0b', '').zfill(padding * byte_length)

			dot_matrix.append(line)

		# for line in dot_matrix:
		# 	print(line)

		new_dot_matrix = []
		for page in range(pages):
			for x in range(bbx_width):
				new_line = ''
				for y in range(byte_length - 1, -1, -1):
					try:
						new_line += dot_matrix[y + page * byte_length][x]
					except IndexError:
						new_line += '0'
				new_dot_matrix.append(new_line)

		# print(new_dot_matrix)
		for count, new_line in enumerate(new_dot_matrix):
			buffer[count] = int(new_line, 2)

		"""
		单字形数组格式
			1. 紧凑型 utf-8 编码, 0xe59bbd
			2. BBX, [8, 17, 8, -1]
				a. BBX[0]: 实际宽度
				b. BBX[1]: 实际高度
				c. BBX[2]: x 轴偏移量，正数向右，负数向左
				d. BBX[3]: y 轴偏移量，正数向上，负数向下
			3. DWIDTH 距离下一个字形原点的水平间距, 16
			4. 符合 FrameBuffer 格式的 bytearray, bytearray(b'\x00\x03')
		"""
		glyph_list.append([unicode, [bbx_width, bbx_height, bbx_off_x, bbx_off_y], dwidth_x, buffer])

	return glyph_list

def parse(chars):
	if not isinstance(chars, str):
		return None
	
	chars_set = set()
	chars_set.add('？')

	# 过滤重复字符
	for char in chars:
		chars_set.add(char)
	
	"""
	font 格式
		1. 字体尺寸, 24
		2. 字体名称, YouYuan
		3. 字体边界盒 (FontBoundingBox), [24, 26, 0, -5]
		4. 字体数量, 8
		5. 文字列表, ['我', ...]
		6. char 字形，格式见下方注释
	"""
	font_list = [None, None, None, None, chars_set]
	valid_bdf_file = False

	with open(BDF_FILENAME, 'rb') as bdf_file:
		# 试读文件前 3 行获取 bdf 版本号
		# 使用版本号确定是否为 bdf 文件
		for _ in range(3):
			line = bdf_file.readline()

			if line.startswith(BDF_STARTFONT):
				version = line.split()[1]

				if version == BDF_VERSION:
					valid_bdf_file = True

		assert valid_bdf_file is True, ".bdf 文件格式不正确"

		# 试读文件前 50 行，获取 font 格式前 3 项信息
		bdf_file.seek(0)
		for _ in range(50):
			line = bdf_file.readline()

			if line.startswith(BDF_SIZE):
				font_list[FONT_SIZE] = int(line.split()[1])
			elif line.startswith(BDF_FAMILYNAME):
				font_list[FONT_FAMILYNAME] = str(line.split(b'"')[1], 'utf-8')
			elif line.startswith(BDF_FONTBOUNDINGBOX):
				_, x, y, off_x, off_y = line.split()
				font_list[FONT_FONTBOUNDINGBOX] = [int(x), int(y), int(off_x), int(off_y)]
		
		font_list[FONT_CHARS] = len(chars_set)


		"""
		char 格式
			1. Unicode, FF0B
			2. DWIDTH, 24
			3. BBX, [13, 14, 6, 0]
			4. BITMAP, [0x11, 0x22, 0x33, ...]
		"""
		for char in chars_set:
			bdf_file.seek(0)
			char_list = [None, None, None, None]

			char_list[CHAR_UNICODE] = get_utf8(char)
			char_unicode = bytes(str(char.encode('unicode_escape')[2:], 'utf-8').upper(), 'utf-8')
			
			while line := bdf_file.readline():
				if line.startswith(BDF_STARTCHAR + char_unicode):
					while line := bdf_file.readline():
						if line.startswith(BDF_DWIDTH):
							char_list[CHAR_DWIDTH] = int(line.split()[1])
						elif line.startswith(BDF_BBX):
							_, width, height, off_x, off_y = line.split()
							char_list[CHAR_BBX] = [int(width), int(height), int(off_x), int(off_y)]
						elif line.startswith(BDF_BITMAP):
							bitmap = []
							while line := bdf_file.readline():
								if line.startswith(BDF_ENDCHAR):
									char_list[CHAR_BITMAP] = bitmap
									break
								else:
									bitmap.append(line.split(b'\r\n')[0])
							break
					break
					
			font_list.append(char_list)

		return font_list


if __name__ == '__main__':
	char_list = '爱我中华，中国万岁'
	# char_list = '？'
	# char_list = '，'
	bdf_list = parse(char_list)

	if bdf_list:
		# for item in bdf_list:
		# 	print(item)

		glyph_list = convert(bdf_list)

		if glyph_list:
			# for glyph in glyph_list:
			# 	print(glyph)

			filename = f'{bdf_list[FONT_FAMILYNAME]}__size_{bdf_list[FONT_SIZE]}__chars_{bdf_list[FONT_CHARS]}.py'
			output_glyph(filename, glyph_list)
